﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Windows.Input;
using Dispatcher.Extensions;
using Dispatcher.Helpers;
using Foundation;
using Repository;

namespace Dispatcher.ViewModels
{
    /// <summary>
    /// Object Set View Model.
    /// </summary>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    internal class EntityListViewModel<TEntity> : ViewModel where TEntity : EntityObject
    {
        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="EntityListViewModel&lt;TEntity&gt;"/> class.
        /// </summary>
        /// <param name="modelContainer">The model container.</param>
        public EntityListViewModel(ModelContainer modelContainer)
        {
            ModelContainer = modelContainer;
            ObjectSet = modelContainer.GetObjectSet<TEntity>();
            ObjectSet.Refresh();
            Items = ObjectSet.ItemsAsObservableCollection();
            DeletedItems = ObjectSet.DeletedItemsAsObservableCollection();
            ApplyChanges = new RoutedCommand("ApplyChanges", GetType());
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, SaveChangesExecute, HasChanges));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, UndoChangesExecute, HasChanges));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="EntityListViewModel&lt;TEntity&gt;"/> class.
        /// </summary>
        /// <param name="entityCollection">The entity collection.</param>
        public EntityListViewModel(EntityCollection<TEntity> entityCollection)
        {
            Items = entityCollection;
            DeletedItems = entityCollection.DeletedItemsAsObservableCollection();
            ApplyChanges = new RoutedCommand("ApplyChanges", GetType());
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, SaveChangesExecute, HasChanges));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, UndoChangesExecute, HasChanges));
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the apply changes.
        /// </summary>
        /// <value>
        /// The apply changes.
        /// </value>
        public ICommand ApplyChanges { get; set; }

        /// <summary>
        /// Gets the model container.
        /// </summary>
        public ObjectContext ModelContainer { get; private set; }

        /// <summary>
        /// Gets the object set.
        /// </summary>
        public ObjectSet<TEntity> ObjectSet { get; private set; }

        /// <summary>
        /// Gets the items.
        /// </summary>
        public IEnumerable<TEntity> Items { get; private set; }

        /// <summary>
        /// Gets the deleted items.
        /// </summary>
        public ObservableCollection<TEntity> DeletedItems { get; private set; }

        #endregion

        #region Methods

        /// <summary>
        /// Saves the changes execute.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void SaveChangesExecute(object sender, ExecutedRoutedEventArgs e)
        {
            var args = new ExtendedEventArgs(e);

            try
            {
                ModelContainer.SaveChanges();
                DeletedItems.Clear();
            }
            catch (Exception exception)
            {
                args.Exception = exception;
            }
        }

        /// <summary>
        /// Undoes the execute.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void UndoChangesExecute(object sender, ExecutedRoutedEventArgs e)
        {
            var args = new ExtendedEventArgs(e);

            try
            {
                ModelContainer.Undo();
                ModelContainer.Refresh(RefreshMode.StoreWins, Items.Where(i => i.EntityState == EntityState.Modified));
                //Items.AddRange(DeletedItems);
                //DeletedItems.Clear();
            }
            catch (Exception exception)
            {
                args.Exception = exception;
            }
        }

        /// <summary>
        /// Determines whether the specified sender has changes.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void HasChanges(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ModelContainer.HasChanges();
        }

        #endregion
    }
}
